/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE uart sample of client. \n
 *
 * History: \n
 * 2023-04-03, Create file. \n
 */
#include "common_def.h"
#include "soc_osal.h"
#include "securec.h"
#include "product.h"
#include "bts_le_gap.h"

#include "sle_device_discovery.h"
#include "sle_connection_manager.h"
#include "sle_client.h"
#include "sle_node_list.h"

#include "osal_timer.h"

#define SLE_MTU_SIZE_DEFAULT 520
#define SLE_SEEK_INTERVAL_DEFAULT 100
#define SLE_SEEK_WINDOW_DEFAULT 100
#define SLE_SEEK_CLOSE_DELAY 500 // 扫描延时关闭定时器时间 500ms
#define UUID_16BIT_LEN 2
#define UUID_128BIT_LEN 16
#define sle_TASK_DELAY_MS 1000
#define sle_WAIT_SLE_CORE_READY_MS 5000
#define sle_RECV_CNT 1000
#define sle_LOW_LATENCY_2K 2000

#define CODE_LOG "[sle client]"

static ssapc_find_service_result_t g_sle_find_service_result = {0};
static sle_announce_seek_callbacks_t g_sle_seek_cbk = {0};
static sle_connection_callbacks_t g_sle_connect_cbk = {0};
static ssapc_callbacks_t g_sle_ssapc_cbk = {0};

// SLE设备连接信息
static sle_addr_t g_sle_remote_addr = {0};
static char g_sle_remote_type[SLE_ADV_VALUE_MAX_LENGTH] = {0};

ssapc_write_param_t g_sle_send_param = {0};

void sle_client_sample_seek_timer_handler(unsigned long data);

osal_timer g_sle_client_seek_timer = {
    .handler = sle_client_sample_seek_timer_handler,
    .interval = SLE_SEEK_CLOSE_DELAY,
    .timer = NULL,
    .data = 0,
};

ssapc_write_param_t *get_g_sle_send_param(void)
{
    return &g_sle_send_param;
}

// 配置SLE扫描参数，并开启器扫描
void sle_start_scan(void)
{
    sle_seek_param_t param = {0};
    param.own_addr_type = 0;
    param.filter_duplicates = 0;
    param.seek_filter_policy = 0;
    param.seek_phys = 1;
    param.seek_type[0] = 1;
    param.seek_interval[0] = SLE_SEEK_INTERVAL_DEFAULT;
    param.seek_window[0] = SLE_SEEK_WINDOW_DEFAULT;
    sle_set_seek_param(&param);

    // 开启扫描
    sle_start_seek();

    // 开启定时器，用于定时关闭设备扫描
    g_sle_client_seek_timer.timer = NULL; // 定时器未初始化
    g_sle_client_seek_timer.interval = SLE_SEEK_CLOSE_DELAY;
    if (osal_timer_init(&g_sle_client_seek_timer) == OSAL_FAILURE) // 初始化定时器
    {
        osal_printk("osal_timer_init Error.\r\n");
    }
    if (osal_timer_start(&g_sle_client_seek_timer) == OSAL_FAILURE) // 启动定时器
    {
        osal_printk("osal_timer_start Error.\r\n");
    }
}

static void sle_client_sample_sle_enable_cbk(errcode_t status)
{
    osal_printk("sle enable: %d.\r\n", status);
    sle_start_scan();
}

static void sle_client_sample_seek_enable_cbk(errcode_t status)
{
    if (status != 0)
    {
        osal_printk("%s sle_client_sample_seek_enable_cbk,status error\r\n", CODE_LOG);
    }
}

// 扫描数据处理
static uint8_t sle_client_seek_result_data_processing(uint8_t *seek_result_data, uint16_t seek_result_data_len, struct sle_adv_common_value *sle_adv_data, uint8_t sle_adv_data_len)
{
    if (seek_result_data == NULL || seek_result_data_len == 0 || sle_adv_data == NULL)
    {
        osal_printk("%s sle_client_seek_result_data_processing param error\r\n", CODE_LOG);
        return 0;
    }

    // 信息打印
    // osal_printk("%s seek_result_data_len = %d\r\n", CODE_LOG, seek_result_data_len);
    // osal_printk("%s seek_result_data: ", CODE_LOG);
    // for (uint8_t j = 0; j < seek_result_data_len; j++)
    // {
    //     osal_printk("0x%02x ", seek_result_data[j]);
    // }
    // osal_printk("\r\n");

    // 数据处理
    uint16_t index = 0;
    uint8_t i = 0;
    for (i = 0; i < sle_adv_data_len; i++)
    {
        if (seek_result_data_len - index < 3)
        {
            break;
        }

        // 读取包类型和长度
        sle_adv_data[i].type = seek_result_data[index++];
        sle_adv_data[i].length = seek_result_data[index++];

        // 读取包内容
        if (sle_adv_data[i].length > SLE_ADV_VALUE_MAX_LENGTH || sle_adv_data[i].length - 1 > seek_result_data_len - index)
        {
            osal_printk("%s sle_adv_data length error\r\n", CODE_LOG);
            break;
        }
        memset(&(sle_adv_data[i].value), 0, SLE_ADV_VALUE_MAX_LENGTH);
        errcode_bt_t ret = memcpy_s(&(sle_adv_data[i].value), SLE_ADV_VALUE_MAX_LENGTH, &(seek_result_data[index]), sle_adv_data[i].length - 1);
        if (ret != EOK)
        {
            osal_printk("%s sle_adv_data[i].value memcpy fail\r\n", CODE_LOG, i);
            break;
        }

        // 计算下一个包的起始位置
        index += sle_adv_data[i].length - 1;

        // osal_printk("%s sle_adv_data[%d].type = 0x%02x\n", CODE_LOG, i, sle_adv_data[i].type);
        // osal_printk("%s sle_adv_data[%d].length = 0x%02x\n", CODE_LOG, i, sle_adv_data[i].length);
        // osal_printk("%s sle_adv_data[%d].value = ", CODE_LOG, i);
        // for (int j = 0; j < sle_adv_data[i].length - 1; j++)
        // {
        //     osal_printk("0x%02x ", sle_adv_data[i].value[j]);
        // }
        // osal_printk("\n");
    }

    return i;
}

// 扫描结果回调函数
static void sle_client_sample_seek_result_info_cbk(sle_seek_result_info_t *seek_result_data)
{
    // 回调参数错误
    if (seek_result_data == NULL)
    {
        osal_printk("%s status error\r\n", CODE_LOG);
        return;
    }

    // 打印扫描到的设备地址
    // osal_printk("%s sle_sample_seek_result_info_cbk  [%02x,%02x,%02x,%02x,%02x,%02x]\n", CODE_LOG,
    //             seek_result_data->addr.addr[0],
    //             seek_result_data->addr.addr[1],
    //             seek_result_data->addr.addr[2],
    //             seek_result_data->addr.addr[3],
    //             seek_result_data->addr.addr[4],
    //             seek_result_data->addr.addr[5]);

    // 将扫描结果数据进行解包
    struct sle_adv_common_value sle_adv_data[8]; // 广播数据集合
    uint8_t count = sle_client_seek_result_data_processing(seek_result_data->data, seek_result_data->data_length, sle_adv_data, 8);

    // for (uint8_t i = 0; i < count; i++)
    // {
    //     // 打印广播数据
    //     osal_printk("%s sle_adv_data[%d].type = 0x%02x\n", CODE_LOG, i, sle_adv_data[i].type);
    //     osal_printk("%s sle_adv_data[%d].length = 0x%02x\n", CODE_LOG, i, sle_adv_data[i].length);
    //     osal_printk("%s sle_adv_data[%d].value = ", CODE_LOG, i);
    //     for (uint8_t j = 0; j < sle_adv_data[i].length - 1; j++)
    //     {
    //         osal_printk("0x%02x ", sle_adv_data[i].value[j]);
    //     }
    //     osal_printk("\n");
    // }

    // 找到设备名称包
    uint8_t Name_count = 0;
    for (Name_count = 0; Name_count < count; Name_count++)
    {
        if (sle_adv_data[Name_count].type == SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME)
        {
            break;
        }
    }

    // 找到设备类型包
    uint8_t Type_count = 0;
    for (Type_count = 0; Type_count < count; Type_count++)
    {
        if (sle_adv_data[Type_count].type == SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA)
        {
            break;
        }
    }

    // 判断是否可连接设备
    if (g_sle_NodeList_count < SLE_CLIENT_MAX_CON)
    {
        // 判断服务器名
        osal_printk("%s Name_count = %d, Type_count = %d, sle_adv_data[Name_count].value:%s\n", CODE_LOG, Name_count, Type_count, sle_adv_data[Name_count].value);
        if (Type_count < count && Name_count < count && strcmp((char *)sle_adv_data[Name_count].value, CONFIG_SLE_SERVER_NAME) == 0) // 做精准匹配
        {
            osal_printk("%s will connect dev\n", CODE_LOG);

            (void)memcpy_s(&g_sle_remote_addr, sizeof(sle_addr_t), &seek_result_data->addr, sizeof(sle_addr_t));
            memset(g_sle_remote_type, 0, sizeof(g_sle_remote_type));
            (void)memcpy_s(g_sle_remote_type, sizeof(g_sle_remote_type), sle_adv_data[Type_count].value, sle_adv_data[Type_count].length);

            sle_stop_seek();
        }
        else
        {
            // osal_printk("%s Continue scanning\r\n", CODE_LOG);
        }
    }
    else
    {
        osal_printk("%s max connect num\r\n", CODE_LOG);
    }
}

// 停止扫描回调处理函数
static void sle_client_sample_seek_disable_cbk(errcode_t status)
{
    if (status != 0)
    {
        osal_printk("%s sle_client_sample_seek_disable_cbk, status error = %x\r\n", CODE_LOG, status);
    }
    else
    {
        sle_connect_remote_device(&g_sle_remote_addr); // 发送连接请求
    }
}

static void sle_client_sample_seek_cbk_register(void)
{
    g_sle_seek_cbk.sle_enable_cb = sle_client_sample_sle_enable_cbk;
    g_sle_seek_cbk.seek_enable_cb = sle_client_sample_seek_enable_cbk;
    g_sle_seek_cbk.seek_result_cb = sle_client_sample_seek_result_info_cbk;
    g_sle_seek_cbk.seek_disable_cb = sle_client_sample_seek_disable_cbk;
    sle_announce_seek_register_callbacks(&g_sle_seek_cbk);
}

// 连接状态改变回调函数
static void sle_client_sample_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr,
                                                        sle_acb_state_t conn_state, sle_pair_state_t pair_state,
                                                        sle_disc_reason_t disc_reason)
{
    unused(addr);
    unused(pair_state);
    osal_printk("%s conn state changed disc_reason:0x%x\r\n", CODE_LOG, disc_reason);

    // 打印连接的设备地址
    osal_printk("%s sle_client_sample_connect_state_changed_cbk addr [%02x,%02x,%02x,%02x,%02x,%02x]\n", CODE_LOG,
                addr->addr[0],
                addr->addr[1],
                addr->addr[2],
                addr->addr[3],
                addr->addr[4],
                addr->addr[5]);

    if (conn_state == SLE_ACB_STATE_CONNECTED) // 连接成功状态
    {
        osal_printk("%s SLE_ACB_STATE_CONNECTED\n", CODE_LOG);

        // 记录设备连接信息
        SLE_NodeList_Add(conn_id, g_sle_remote_type, strlen(g_sle_remote_type), addr);
        SLE_NodeList_Print();

        // 连接后不进行配对，直接发送交换信息请求
        ssap_exchange_info_t info = {0};
        info.mtu_size = SLE_MTU_SIZE_DEFAULT;
        info.version = 1;
        ssapc_exchange_info_req(1, conn_id, &info);

        // 连接完成后，重新开启扫描
        sle_start_seek(); // 重新开启扫描
        if (osal_timer_mod(&g_sle_client_seek_timer, SLE_SEEK_CLOSE_DELAY) != OSAL_SUCCESS)
        {
            osal_printk("%s osal_timer_mod failed\r\n", CODE_LOG);
        }
    }
    else if (conn_state == SLE_ACB_STATE_NONE) // 未连接状态
    {
        osal_printk("%s SLE_ACB_STATE_NONE\r\n", CODE_LOG);
    }
    else if (conn_state == SLE_ACB_STATE_DISCONNECTED) // 断开连接状态
    {
        osal_printk("%s SLE_ACB_STATE_DISCONNECTED\r\n", CODE_LOG);

        // 连接断开，在列表中移除对应节点信息
        SLE_NodeList_Remove(addr);
        SLE_NodeList_Print();

        // 连接断开后，重新开启扫描
        sle_start_scan();
    }
    else
    {
        osal_printk("%s status error\r\n", CODE_LOG);
    }
}

// 配对完成回调函数
void sle_client_sample_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
{
    // osal_printk("%s pair complete conn_id:%d, addr:%02x***%02x%02x\n", CODE_LOG, conn_id, addr->addr[0], addr->addr[4], addr->addr[5]);
    if (status == 0)
    {
        ssap_exchange_info_t info = {0};
        info.mtu_size = SLE_MTU_SIZE_DEFAULT;
        info.version = 1;
        ssapc_exchange_info_req(0, conn_id, &info);
    }
}

static void sle_client_sample_connect_cbk_register(void)
{
    g_sle_connect_cbk.connect_state_changed_cb = sle_client_sample_connect_state_changed_cbk;
    g_sle_connect_cbk.pair_complete_cb = sle_client_sample_pair_complete_cbk;
    sle_connection_register_callbacks(&g_sle_connect_cbk);
}

static void sle_client_sample_exchange_info_cbk(uint8_t client_id, uint16_t conn_id, ssap_exchange_info_t *param,
                                                errcode_t status)
{
    // osal_printk("%s exchange_info_cbk,pair complete client id:%d status:%d\r\n", CODE_LOG, client_id, status);
    // osal_printk("%s exchange mtu, mtu size: %d, version: %d.\r\n", CODE_LOG, param->mtu_size, param->version);
    ssapc_find_structure_param_t find_param = {0};
    find_param.type = SSAP_FIND_TYPE_PROPERTY;
    find_param.start_hdl = 1;
    find_param.end_hdl = 0xFFFF;
    ssapc_find_structure(0, conn_id, &find_param);
}

static void sle_client_sample_find_structure_cbk(uint8_t client_id, uint16_t conn_id,
                                                 ssapc_find_service_result_t *service,
                                                 errcode_t status)
{
    // osal_printk("%s find structure cbk client: %d conn_id:%d status: %d \r\n", CODE_LOG, client_id, conn_id, status);
    // osal_printk("%s find structure start_hdl:[0x%02x], end_hdl:[0x%02x], uuid len:%d\r\n", CODE_LOG, service->start_hdl, service->end_hdl, service->uuid.len);
    g_sle_find_service_result.start_hdl = service->start_hdl;
    g_sle_find_service_result.end_hdl = service->end_hdl;
    memcpy_s(&g_sle_find_service_result.uuid, sizeof(sle_uuid_t), &service->uuid, sizeof(sle_uuid_t));
}

static void sle_client_sample_find_property_cbk(uint8_t client_id, uint16_t conn_id,
                                                ssapc_find_property_result_t *property, errcode_t status)
{
    // osal_printk("%s sle_client_sample_find_property_cbk, client id: %d, conn id: %d, operate ind: %d, "
    //             "descriptors count: %d status:%d property->handle %d\r\n",
    //             CODE_LOG,
    //             client_id, conn_id, property->operate_indication,
    //             property->descriptors_count, status, property->handle);
    g_sle_send_param.handle = property->handle;
    g_sle_send_param.type = SSAP_PROPERTY_TYPE_VALUE;
}

static void sle_client_sample_find_structure_cmp_cbk(uint8_t client_id, uint16_t conn_id,
                                                     ssapc_find_structure_result_t *structure_result,
                                                     errcode_t status)
{
    unused(conn_id);
    osal_printk("%s sle_client_sample_find_structure_cmp_cbk,client id:%d status:%d type:%d uuid len:%d \r\n",
                CODE_LOG, client_id, status, structure_result->type, structure_result->uuid.len);
}

static void sle_client_sample_write_cfm_cb(uint8_t client_id, uint16_t conn_id,
                                           ssapc_write_result_t *write_result, errcode_t status)
{
    osal_printk("%s sle_client_sample_write_cfm_cb, conn_id:%d client id:%d status:%d handle:%02x type:%02x\r\n",
                CODE_LOG, conn_id, client_id, status, write_result->handle, write_result->type);
}

static void sle_client_sample_ssapc_cbk_register(ssapc_notification_callback notification_cb,
                                                 ssapc_notification_callback indication_cb)
{
    g_sle_ssapc_cbk.exchange_info_cb = sle_client_sample_exchange_info_cbk;
    g_sle_ssapc_cbk.find_structure_cb = sle_client_sample_find_structure_cbk;
    g_sle_ssapc_cbk.ssapc_find_property_cbk = sle_client_sample_find_property_cbk;
    g_sle_ssapc_cbk.find_structure_cmp_cb = sle_client_sample_find_structure_cmp_cbk;
    g_sle_ssapc_cbk.write_cfm_cb = sle_client_sample_write_cfm_cb;
    g_sle_ssapc_cbk.notification_cb = notification_cb;
    g_sle_ssapc_cbk.indication_cb = indication_cb;
    ssapc_register_callbacks(&g_sle_ssapc_cbk);
}

// SLE客户端扫描定时关闭 回调函数
void sle_client_sample_seek_timer_handler(unsigned long data)
{
    osal_printk("%s sle_client_sample_seek_timer_handler\n", CODE_LOG);

    // 关闭SLE扫描
    if (sle_stop_seek() == ERRCODE_SUCC)
    {
        osal_printk("%s Stop Seek Success!\n", CODE_LOG);
    }
    else
    {
        osal_printk("%s Stop Seek Failed!\n", CODE_LOG);
    }

    // 销毁定时器
    osal_timer_destroy(&g_sle_client_seek_timer);
}

void sle_client_init(ssapc_notification_callback notification_cb, ssapc_indication_callback indication_cb)
{
    // 初始化SLE节点存储链表
    SLE_NodeList_Init();

    (void)osal_msleep(1000); /* 延时5s，等待SLE初始化完毕 */
    osal_printk("[SLE Client] try enable.\r\n");
    sle_client_sample_seek_cbk_register();
    sle_client_sample_connect_cbk_register();
    sle_client_sample_ssapc_cbk_register(notification_cb, indication_cb);

    if (enable_sle() != ERRCODE_SUCC)
    {
        osal_printk("[SLE Client] sle enbale fail !\r\n");
    }
}